﻿using Microsoft.Practices.Prism.Commands;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;

namespace HobSoft.SoccerBet2014.Client.ViewModels
{
    using Common;
    using Domain.ClientService;
    using Domain.Factory;
    using Domain.Implementation;
    using Domain.IRepositories;
    using Domain.Schedule;
    using SoccerBet2014.Common.Collection;
    using dm = Domain.Models;
    public class AccountShellModel : CommandViewModel, IDisposable
    {
        #region command
        public ICommand SignupAllCommand { get; protected set; }
        public ICommand SignoutAllCommand { get; protected set; }
        public ICommand QueryBookmakerCommand { get; protected set; }
        public ICommand LoadAllCommand { get; protected set; }
        public ICommand SaveAllCommand { get; protected set; }
        #endregion

        #region cort & initialize
        protected readonly IBetSiteProxyService betSiteProxyService;
        protected readonly ISystemService sysService;
        protected readonly IBfRequestService bfRequestService;
        protected readonly IBookmakerRepository bmRepository;
        protected readonly IBmAccountRepository accountRepository;
        protected readonly IBFAccountRepository bfAccountRepository;
        protected readonly IScheduleTimerFactory timerFactory;
        protected readonly IBetSiteServiceCallback betSiteCallback;
        public AccountShellModel(IBetSiteProxyService _betSiteProxyService
                                                , IScheduleTimerFactory _timerFactory
                                                , ISystemService _sysService
                                                , IBfRequestService _bfRequestService
                                                , IBetSiteServiceCallback _betSiteCallback
                                                , IBookmakerRepository _bmRepository
                                                , IBmAccountRepository _accountRepository
                                                , IBFAccountRepository _bfAccountRepository)
        {
            betSiteProxyService = _betSiteProxyService;
            timerFactory = _timerFactory;
            sysService = _sysService;
            bfRequestService = _bfRequestService;
            bmRepository = _bmRepository;
            accountRepository = _accountRepository;
            bfAccountRepository = _bfAccountRepository;
            betSiteCallback = _betSiteCallback;

            bookmakers = new ObservableCollection<Bookmaker>();
            InitializeCommand();
            InitializeModels();
        }

        protected void InitializeCommand()
        {
            SignupAllCommand = new DelegateCommand(SignupAll, CanSignupAll);
            SignoutAllCommand = new DelegateCommand(SignoutAll, CanSignoutAll);
            LoadAllCommand = new DelegateCommand(LoadAll, CanLoadAll);
            SaveAllCommand = new DelegateCommand(SaveAll, CanSaveAll);
            QueryBookmakerCommand = new DelegateCommand(QueryBookmaker, CanQueryBookmaker);

            RegisterCommand(SignupAllCommand, "登录所有账号");
            RegisterCommand(SignoutAllCommand, "注销所有账号");
            RegisterCommand(LoadAllCommand, "加载所有账号");
            RegisterCommand(SaveAllCommand, "保存所有账号");
            RegisterCommand(QueryBookmakerCommand, "同步博彩网站列表");
        }

        protected void InitializeModels()
        {
            bookmakers.CollectionChanged += Bookmakers_CollectionChanged;
            Accounts = new ObservableCollection<BmAccount>();
            Accounts.CollectionChanged += Accounts_CollectionChanged;
            Bookmakers = new ReadOnlyObservableCollection<Bookmaker>(bookmakers);
            BFAccounts = new ObservableCollection<BfAccount>();
            BFAccounts.CollectionChanged += BFAccounts_CollectionChanged;

            var betSiteCallbackBind = betSiteCallback as Service.BetSiteServiceCallback;
            if (betSiteCallback != null)
                betSiteCallbackBind.OnSiteLogChanged += BetSiteCallback_OnSiteLogChanged;
            State = ModelState.Initializing;
            BackgroundWorker bWork = new BackgroundWorker();
            bWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sde, dwev) =>
            {
                bWork.Dispose();
            });
            bWork.DoWork += new DoWorkEventHandler((sde, dwev) =>
            {
                var bmlist = bmRepository.GetMany(b => b.Available);
                var actlist = accountRepository.GetMany(a => true);
                var bfactlist = bfAccountRepository.GetMany(a => true);

                Application.Current.Dispatcher.Invoke(new Action(delegate()
                {
                    var vmbmlist = AutoMapper.Mapper.Map<List<Bookmaker>>(bmlist);
                    var vmactlist = AutoMapper.Mapper.Map<List<BmAccount>>(actlist);
                    var vmbfactlist = AutoMapper.Mapper.Map<List<BfAccount>>(bfactlist);
                    foreach (var item in vmbmlist)
                    {
                        bookmakers.Add(item);
                    }
                    foreach (var item in vmactlist)
                    {
                        Accounts.Add(item);
                    }
                    foreach (var item in vmbfactlist)
                    {
                        BFAccounts.Add(item);
                    }

                    RaiseCommandCanExecute(SignupAllCommand);
                    RaiseCommandCanExecute(SignoutAllCommand);
                    RaiseCommandCanExecute(LoadAllCommand);
                    RaiseCommandCanExecute(SaveAllCommand);
                    RaiseCommandCanExecute(QueryBookmakerCommand);
                    State = ModelState.Freeing;
                }));
                RequestRemoteBookmaker();
            });
            bWork.RunWorkerAsync();

        }

        protected override bool HasAggregationExecuteing()
        {
            return Accounts.Any(a => a.HasCommandExecuting) ||
                BFAccounts.Any(a => a.HasCommandExecuting);
        }

        protected override CommandState GetAggregationExecutingCommand()
        {
            var actExecuting = Accounts.FirstOrDefault(a => a.HasCommandExecuting);
            var bfactExecuting = BFAccounts.FirstOrDefault(a => a.HasCommandExecuting);
            return actExecuting != null ? actExecuting.ExecutingCommand :
                        bfactExecuting != null ? bfactExecuting.ExecutingCommand : null;
        }

        #endregion

        #region event handler
        private void BetSiteCallback_OnSiteLogChanged(byte bmId, string account, BetSiteRequestType type, DateTime lastUpdateTime)
        {
            var accountObject = Accounts.FirstOrDefault(a => a.BmID == bmId && a.Account.Equals(account, StringComparison.OrdinalIgnoreCase));
            if (accountObject == null) return;
            accountObject.ResetUpdateState(type, lastUpdateTime);
        }

        private void BFAccounts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Replace)
            {
                foreach (BfAccount item in e.NewItems)
                {
                    item.OnDeleteRequest += BfAccount_OnDeleteRequest;
                    item.OnCommandStart += BfAccount_OnCommandOver;
                    item.OnCommandOver += BfAccount_OnCommandOver;
                    item.bfRequestService = bfRequestService;
                }
            }
        }

        private void BfAccount_OnCommandOver(object sender, CommandState e)
        {
            RaisePropertyChanged("HasCommandExecuting");
            RaisePropertyChanged("ExecutingCommandMessage");
            RaisePropertyChanged("ExecutingCommand");
            RaiseCommandCanExecute(SignoutAllCommand);
            RaiseCommandCanExecute(SignupAllCommand);
        }

        private void BfAccount_OnCommandStart(object sender, CommandState e)
        {
            RaisePropertyChanged("HasCommandExecuting");
            RaisePropertyChanged("ExecutingCommandMessage");
            RaisePropertyChanged("ExecutingCommand");
        }

        private void BfAccount_OnDeleteRequest(object sender, BfAccount delAccount)
        {
            if (BFAccounts.Contains(delAccount) && !delAccount.OnSignup)
            {
                if (MessageBox.Show(string.Format("是否删除账号{0}", delAccount.Account), "删除确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                    BFAccounts.Remove(delAccount);
                delAccount.OnDeleteRequest -= BfAccount_OnDeleteRequest;
                delAccount.OnCommandStart -= BfAccount_OnCommandStart;
                delAccount.OnCommandOver -= BfAccount_OnCommandOver;
                delAccount.Signout();
            }
        }

        private void Bookmakers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
        }

        private void Accounts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Replace)
            {
                foreach (BmAccount item in e.NewItems)
                {
                    item.OnDeleteRequest += Account_OnDeleteRequest;
                    item.OnCommandStart += Account_OnCommandStart;
                    item.OnCommandOver += Account_OnCommandOver;
                    item.Bookmakers = new ReadOnlyObservableCollection<Bookmaker>(bookmakers);
                    item.BetManagerProxy = this.betSiteProxyService;
                    item.ScheduleTimer = timerFactory.CreateOrGet();
                }
            }
        }

        private void Account_OnCommandOver(object sender, CommandState e)
        {
            RaisePropertyChanged("HasCommandExecuting");
            RaisePropertyChanged("ExecutingCommandMessage");
            RaisePropertyChanged("ExecutingCommand");
            RaiseCommandCanExecute(SignoutAllCommand);
            RaiseCommandCanExecute(SignupAllCommand);
        }

        private void Account_OnCommandStart(object sender, CommandState e)
        {
            RaisePropertyChanged("HasCommandExecuting");
            RaisePropertyChanged("ExecutingCommandMessage");
            RaisePropertyChanged("ExecutingCommand");
        }

        private void Account_OnDeleteRequest(object sender, BmAccount delAccount)
        {
            if (Accounts.Contains(delAccount) && !delAccount.OnSignup)
            {
                if (MessageBox.Show(string.Format("是否删除账号{0}", delAccount.Account), "删除确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                {
                    Accounts.Remove(delAccount);
                    delAccount.OnDeleteRequest -= Account_OnDeleteRequest;
                    delAccount.OnCommandStart -= Account_OnCommandStart;
                    delAccount.OnCommandOver -= Account_OnCommandOver;
                    delAccount.StopSchedule();
                    delAccount.Dispose();
                }
            }
        }
        #endregion

        #region model property

        private ObservableCollection<BfAccount> _bfaccount;

        public ObservableCollection<BfAccount> BFAccounts
        {
            get { return _bfaccount; }
            protected set
            {
                PropertyValueChange(ref _bfaccount, value, "BFAccounts");
            }
        }
        

        private ObservableCollection<BmAccount> _accounts;
        public ObservableCollection<BmAccount> Accounts
        {
            get
            {
                return _accounts;
            }
            protected set
            {
                PropertyValueChange(ref _accounts, value, "Accounts");
            }
        }

        protected readonly ObservableCollection<Bookmaker> bookmakers;
        private ReadOnlyObservableCollection<Bookmaker> _bookmakers;
        public ReadOnlyObservableCollection<Bookmaker> Bookmakers
        {
            get
            {
                return _bookmakers;
            }
            protected set
            {
                PropertyValueChange(ref _bookmakers, value, "Bookmakers");
            }
        }
        #endregion

        #region inner method
        protected void RequestRemoteBookmaker()
        {
            var sysbmlist = sysService.GetBookmaker();
            var bmlist = bmRepository.GetMany(b => b.Available);
            var newbms = sysbmlist.Where(b => !bmlist.Contains(b, new LambdaEqualityComparer<dm.Bookmaker>((x, y) => x.BmID == y.BmID))).ToList();
            if (newbms == null || !newbms.Any())
                return;
            bmRepository.AddOrUpdate(newbms);
            BetApplication.SubmitCacheModel<dm.Bookmaker>();
            var vmbmlist = AutoMapper.Mapper.Map<List<Bookmaker>>(newbms);
            Application.Current.Dispatcher.Invoke(new Action(delegate()
            {
                foreach (var item in vmbmlist)
                {
                    bookmakers.Add(item);
                }
            }));
        }
        #endregion

        #region command imple
        protected bool CanQueryBookmaker()
        {
            return OnInitialized && State != ModelState.Initializing && !CommandOnExecuting(QueryBookmakerCommand);
        }

        public void QueryBookmaker()
        {
            RaiseCommandStart(QueryBookmakerCommand, "同步查询Bookmaker列表开始");
            RaiseCommandCanExecute(QueryBookmakerCommand);
            Task.Factory.StartNew(() =>
            {
                RequestRemoteBookmaker();
            }).ContinueWith((t) =>
            {
                RaiseCommandOver(QueryBookmakerCommand, "同步查询Bookmaker列表结束");
                RaiseCommandCanExecute(QueryBookmakerCommand);
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

        protected bool CanLoadAll()
        {
            return OnInitialized && State != ModelState.Initializing && !CommandOnExecuting(LoadAllCommand);
        }
        public void LoadAll()
        {
            RaiseCommandStart(LoadAllCommand, "加载账号配置开始");
            RaiseCommandCanExecute(LoadAllCommand);
            BinaryContext.Current.Initialize<dm.Bookmaker, byte>();
            BinaryContext.Current.Initialize<dm.BmAccount, string>();
            BinaryContext.Current.Initialize<dm.BFAccount, string>();
            var bmlist = bmRepository.GetMany(b => b.Available);
            var acountlist = accountRepository.GetMany(a => true);
            var bfactlist = bfAccountRepository.GetMany(a => true);
            var vmbmlist = AutoMapper.Mapper.Map<List<Bookmaker>>(bmlist);
            var dmactlist = AutoMapper.Mapper.Map<List<BmAccount>>(acountlist);
            var dmbfalist = AutoMapper.Mapper.Map<List<BfAccount>>(bfactlist);

            bookmakers.Clear();
            _accounts.Clear();
            foreach (var item in vmbmlist)
            {
                bookmakers.Add(item);
            }
            foreach (var item in dmactlist)
            {
                _accounts.Add(item);
            }
            foreach (var item in dmbfalist)
            {
                _bfaccount.Add(item);
            }
            RaiseCommandOver(LoadAllCommand, "加载账号配置完成");
            RaiseCommandCanExecute(LoadAllCommand);
        }

        protected bool CanSaveAll()
        {
            return OnInitialized && State != ModelState.Initializing && !CommandOnExecuting(SaveAllCommand);
        }
        public void SaveAll()
        {
            RaiseCommandStart(SaveAllCommand, "保存账号配置开始");
            RaiseCommandCanExecute(SaveAllCommand);
            var dmbmlist = AutoMapper.Mapper.Map<List<dm.Bookmaker>>(bookmakers.Where(b => !string.IsNullOrEmpty(b.DefaultHost)).ToList());
            var dmaclist = AutoMapper.Mapper.Map<List<dm.BmAccount>>(_accounts.Where(a => a.BmID != 0
                                                    && !string.IsNullOrEmpty(a.Account)
                                                    && !string.IsNullOrEmpty(a.Password)).ToList());
            var dmbfalist = AutoMapper.Mapper.Map<List<dm.BFAccount>>(_bfaccount.Where(a => !string.IsNullOrEmpty(a.Account)
                                                    && !string.IsNullOrEmpty(a.Password)
                                                    && !string.IsNullOrEmpty(a.AppKey)
                                                    && !string.IsNullOrEmpty(a.CertificateFile)).ToList());
            var upbfname=dmbfalist.Select(a=>a.Account).ToList();
            var accountComparer = new LambdaEqualityComparer<dm.BmAccount>((x, y) => x.BmID == y.BmID && x.Account.Equals(y.Account, StringComparison.OrdinalIgnoreCase));
            accountRepository.DeleteMany(a => !dmaclist.Contains(a, accountComparer));
            bfAccountRepository.DeleteMany(a => !upbfname.Contains(a.Account));
            bmRepository.AddOrUpdate(dmbmlist);
            accountRepository.AddOrUpdate(dmaclist);
            bfAccountRepository.AddOrUpdate(dmbfalist);
            BetApplication.SubmitCacheModel<dm.Bookmaker>();
            BetApplication.SubmitCacheModel<dm.BmAccount>();
            BetApplication.SubmitCacheModel<dm.BFAccount>();
            RaiseCommandOver(SaveAllCommand, "保存账号配置完成");
            RaiseCommandCanExecute(SaveAllCommand);
        }

        protected bool CanSignupAll()
        {
            var onBmAllSignup = !_accounts.Any(a => !a.OnSignup);
            var onBfAllSignup = !BFAccounts.Any(a => !a.OnSignup);
            return OnInitialized && State != ModelState.Initializing && !CommandOnExecuting(SignupAllCommand) && (!onBfAllSignup || !onBmAllSignup);
        }
        public void SignupAll()
        {
            RaiseCommandStart(SignupAllCommand, "登录所有账号开始");
            RaiseCommandCanExecute(SignupAllCommand);

            foreach (var account in _accounts)
            {
                if (!account.OnSignup)
                    account.Signup();
            }
            foreach (var account in BFAccounts)
            {
                if (!account.OnSignup)
                    account.Signout();
            }
            RaiseCommandOver(SignupAllCommand, "登录所有账号完成");
            RaiseCommandCanExecute(SignupAllCommand);
            RaiseCommandCanExecute(SignoutAllCommand);
        }

        protected bool CanSignoutAll()
        {
            var onBmAnySignup = _accounts.Any(a => a.OnSignup);
            var onBfAnySignup = BFAccounts.Any(a => a.OnSignup);
            return OnInitialized && State != ModelState.Initializing && !CommandOnExecuting(SignoutAllCommand) && (onBmAnySignup || onBfAnySignup);
        }
        public void SignoutAll()
        {
            RaiseCommandStart(SignoutAllCommand, "开始注销所有账号");
            RaiseCommandCanExecute(SignoutAllCommand);
            foreach (var account in _accounts)
            {
                if (account.OnSignup)
                    account.Signout();
            }
            foreach (var account in BFAccounts)
            {
                if (account.OnSignup)
                    account.Signout();
            }
            RaiseCommandOver(SignoutAllCommand, "已经注销所有账号");
            RaiseCommandCanExecute(SignoutAllCommand);
            RaiseCommandCanExecute(SignupAllCommand);
        }
        #endregion


        #region dispose
        public void Dispose()
        {
        }
        #endregion

    }
}
